
import os
import requests
import paho.mqtt.client as mqtt
from PyQt5.QtWidgets import QApplication, QWidget, QMessageBox
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, pyqtSlot, QMutex
import json
import time
from UI import Ui_Form
import sys


class Fw_DownLoad_Task(QThread):
    fw_refresh_signal = pyqtSignal()
    fw_refresh_result_signal = pyqtSignal(list)
    download_signal = pyqtSignal(str)
    server_signal = pyqtSignal(str)
    error_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()

        self.fw_refresh_signal.connect(self.refresh_available_fw)
        self.download_signal.connect(self.download)

        self.file_path = "fw.json"
        # self.file_data = []
        # self.file_data_size = 0

    def refresh_available_fw(self):
        """获取可用固件列表"""
        try:
            response = requests.post(
                "http://127.0.0.1:8000/api/fw_interface",
                json={"action": "show_available_fw"}
            )
            if response.status_code == 200:
                data = response.json()
                self.fw_refresh_result_signal.emit(data["available_fw"])
            else:
                self.error_signal.emit(f"服务器错误: {response.status_code}")
        except Exception as e:
            self.error_signal.emit(f"请求失败: {str(e)}")

    @pyqtSlot(str)
    def download(self, version):
        try:
            # 构建POST请求的JSON数据
            post_data = {
                "action": "transmit_fw",
                "version": version
            }

            # 发送POST请求
            response = requests.post(
                "http://127.0.0.1:8000/api/fw_interface",
                json=post_data
            )

            if response.status_code != 200:
                error_msg = response.json().get("message", "未知错误") if response.content else str(
                    response.status_code)
                self.error_signal.emit(f"下载失败: {error_msg}")
                return

            # 获取二进制数据
            fw_data = response.content.decode("utf-8")
            with open(self.file_path, "w", encoding='utf-8') as f:
                f.write(str(fw_data))

            # # 清空之前的数据
            # self.file_data = []
            # with open(self.file_path, "r", encoding='utf-8') as f:
            #     for line in f:
            #         line = line.strip()  # 去除首尾空白字符
            #         if line:  # 跳过空行
            #             try:
            #                 # 解析JSON数据
            #                 json_obj = json.loads(line)
            #                 self.file_data.append(json_obj)
            #             except json.JSONDecodeError as e:
            #                 self.server_signal.emit(f"解析JSON出错: {e}，行内容: {line}")
            #
            # self.file_data_size = self.file_data[0].get("total_size")
            # print(self.file_data_size)
            # print(len(self.file_data))

        except Exception as e:
            self.server_signal.emit(f"读取文件出错: {str(e)}")


class MQTT_Task(QThread):
    download_info_signal = pyqtSignal(str)
    fw_ready_signal = pyqtSignal(str)
    update_signal = pyqtSignal(str)  # 触发固件更新命令
    ack_signal = pyqtSignal(str)
    progress_signal = pyqtSignal(int)
    server_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self._running = False
        self._client = None
        # MQTT配置
        self.mqtt_config = {
            "broker": "broker.emqx.io",
            "port": 1883,
            "pub_topic": "ESP32C3/ota_device_rec",
            "sub_topic": "ESP32C3/ota_device_send",
            "qos": 1,
            "keepalive": 60
        }

        self.is_updating = False

        self.current_device = None
        self.device_id = None

        self.file_path = "fw.json"
        self.file_data_size = 0
        self.file_data = []
        self.fw_version = ""

        self.download_info_signal.connect(self.ready_download_info)
        self.fw_ready_signal.connect(self.ready_fw)
        self.update_signal.connect(self.start_update)

    def run(self):
        self._running = True
        self._connect_mqtt()
        while self._running:
            time.sleep(0.1)

    def _connect_mqtt(self):
        try:
            self._client = mqtt.Client()
            self._client.on_connect = self._on_connect
            self._client.on_message = self._on_message

            # 连接Broker
            self._client.connect(
                host=self.mqtt_config["broker"],
                port=self.mqtt_config["port"],
                keepalive=self.mqtt_config["keepalive"]
            )
            self._client.loop_start()
        except Exception as e:
            self.ack_signal.emit(f"MQTT连接失败: {str(e)}")

    def _on_connect(self, client, userdata, flags, rc):
        """MQTT连接回调"""
        if rc == 0:
            self.ack_signal.emit("MQTT服务器连接成功")
            client.subscribe(self.mqtt_config["sub_topic"], qos=self.mqtt_config["qos"])
        else:
            self.ack_signal.emit(f"MQTT连接失败，错误码: {rc}")

    def send_data(self, data):
        if self._client and self._client.is_connected():
            self._client.publish(
                self.mqtt_config["pub_topic"],
                data,
                qos=self.mqtt_config["qos"]
            )
        else:
            self.ack_signal.emit("服务器未连接，指令发送失败")

    @pyqtSlot(str)
    def ready_download_info(self, device_id):
        self.device_id = device_id
        print(self.device_id)

    @pyqtSlot(str)
    def ready_fw(self, fw_version):
        self.is_updating = True
        # 清空之前的数据
        self.file_data = []
        with open(self.file_path, "r", encoding='utf-8') as f:
            for line in f:
                line = line.strip()  # 去除首尾空白字符
                if line:  # 跳过空行
                    try:
                        # 解析JSON数据
                        json_obj = json.loads(line)
                        self.file_data.append(json_obj)
                    except json.JSONDecodeError as e:
                        self.server_signal.emit(f"解析JSON出错: {e}，行内容: {line}")

        self.file_data_size = self.file_data[0].get("total_size")
        self.fw_version = fw_version
        print(self.file_data_size)
        print(len(self.file_data))

    def _on_message(self, client, userdata, msg):
        if self.is_updating:
            """MQTT消息接收回调"""
            try:
                payload = msg.payload.decode("utf-8")
                device_ack = json.loads(payload)
                print(device_ack)
                self.ack_signal.emit(str(device_ack))

                if device_ack.get("type") == "request":
                    self.device_id = device_ack.get("device_id", "")
                    first_frame = {
                        "type": "start",
                        "device_id": self.device_id,
                        "version": self.fw_version,
                        "file_size": self.file_data_size,
                        "block_size": 256
                    }
                    print(json.dumps(first_frame))
                    self.send_data(json.dumps(first_frame))
                if device_ack.get("type") == "ack":
                    if device_ack.get("device_id") == self.device_id:
                        if device_ack.get("status") == "ok":
                            if not device_ack.get("end_flag"):
                                seq = device_ack.get("next_seq")
                                if seq == 99999999:
                                    end_signal = {
                                        "type": "end",
                                        "device_id": self.device_id,
                                        "received_frames": len(self.file_data),
                                        "error_msg": ""
                                    }
                                    self.send_data(json.dumps(end_signal).encode("UTF-8"))
                                    self.is_updating = False
                                if seq <= len(self.file_data):
                                    # print(self.file_data[seq - 1])
                                    self.send_data(json.dumps(self.file_data[seq - 1]).encode("UTF-8"))
                                    progress = int(seq / len(self.file_data) * 100)
                                    self.progress_signal.emit(progress)

                        elif device_ack.get("status") == "check_sum_err":
                            self.send_data(json.dumps(self.file_data[device_ack.get("next_seq") - 1]).encode("UTF-8"))

            except Exception as e:
                self.ack_signal.emit(f"消息解析失败: {str(e)}")

    def stop(self):
        """停止线程并清理MQTT连接"""
        self._running = False
        if self._client:
            self._client.loop_stop()
            self._client.disconnect()
        self.quit()

    @pyqtSlot(str)
    def start_update(self, update_cmd):
        if self._client and self._client.is_connected():
            self.current_device = json.loads(update_cmd).get("device_id")
            print(self.current_device)
            self._client.publish(
                self.mqtt_config["pub_topic"],
                update_cmd,
                qos=self.mqtt_config["qos"]
            )
        else:
            self.ack_signal.emit("服务器未连接，指令发送失败")


class My_APP(QWidget, Ui_Form):
    def __init__(self):
        super(My_APP, self).__init__()
        self.setupUi(self)
        self.setLayout(self.verticalLayout)
        self.setWindowTitle("飞鸟OTA")
        self.progressBar.setValue(0)
        self.details_textBrowser.append("确保网络正常，设备id正确")

        self.fw_download_task = Fw_DownLoad_Task()
        self.fw_download_task.fw_refresh_result_signal.connect(self.show_fw_list)
        self.fw_download_task.error_signal.connect(self.get_fw_error_deal)
        self.fw_download_task.start()

        self.mqtt_task = MQTT_Task()
        self.mqtt_task.ack_signal.connect(self.handle_mqtt_msg)
        self.mqtt_task.progress_signal.connect(self.progress_update)
        self.mqtt_task.server_signal.connect(self.handle_server_info)
        self.mqtt_task.start()

        self.refresh_pushButton.clicked.connect(self.refresh_fw_list)
        self.download_pushButton.clicked.connect(self.download_fw_to_mcu)
        self.clear_pushButton.clicked.connect(self.clear_record)

    @pyqtSlot(str)
    def get_fw_error_deal(self, err):
        self.details_textBrowser.append(err)

    @pyqtSlot(list)
    def show_fw_list(self, fw_list):
        self.details_textBrowser.append(f"可用版本：{fw_list}")
        self.version_comboBox.clear()
        for fw in fw_list:
            self.version_comboBox.addItem(fw)

    def refresh_fw_list(self):
        self.fw_download_task.fw_refresh_signal.emit()
        self.fw_download_task.download_signal.emit(self.version_comboBox.currentText())

    def download_fw_to_mcu(self):
        self.mqtt_task.fw_ready_signal.emit(self.version_comboBox.currentText())
        self.mqtt_task.download_info_signal.emit(self.device_id_lineEdit.text())
        cmd = {
            "type": "update",
            "device_id": self.device_id_lineEdit.text()
        }
        print(json.dumps(cmd))
        self.mqtt_task.update_signal.emit(json.dumps(cmd))

    @pyqtSlot(str)
    def handle_mqtt_msg(self, msg):
        print(msg)
        self.details_textBrowser.append(msg)

    @pyqtSlot(int)
    def progress_update(self, progress):
        self.progressBar.setValue(progress)

    @pyqtSlot(str)
    def handle_server_info(self, info):
        self.details_textBrowser.append(info)

    def clear_record(self):
        self.progressBar.setValue(0)
        self.details_textBrowser.clear()


if __name__ == '__main__':
    app = QApplication(sys.argv)  # 用于命令行启动传参
    My_window = My_APP()
    My_window.show()
    sys.exit(app.exec_())
